
/*
    Software License

    Copyright (C) 2021-05-24  Xoronos

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

/*
    Liabilities

    The software is provided "AS IS" without any warranty of any kind, either expressed,
    implied, or statutory, including, but not limited to, any warranty that the software
    will conform to specifications, any implied warranties of merchantability, fitness
    for a particular purpose, and freedom from infringement, and any warranty that the
    documentation will conform to the software, or any warranty that the software will
    be error free.

    In no event shall Xoronos be liable for any damages, including, but not limited to,
    direct, indirect, special or consequential damages, arising out of, resulting from,
    or in any way connected with this software, whether or not based upon warranty,
    contract, tort, or otherwise, whether or not injury was sustained by persons or
    property or otherwise, and whether or not loss was sustained from, or arose out of
    the results of, or use of, the software or services provided hereunder.

    To request the provided software under a different license you can contact us at
    support@xoronos.com
*/

#include "test_common.h"

#define TEST_COMMON_MAX_FILE 262144
#define TEST_COMMON_CMM_FILE 2048

int test_split_block_comp(
 )
{
    uint8_t *ibytes;
    uint8_t *iblock0;
    uint8_t *iblock1;
    FILE *fpi;
    FILE *fpb0;

    uint64_t ibytes_len;
    uint64_t iblock0_len;

    int i, error;
    uint32_t tag;
    uint64_t offset;

    ibytes_len = TEST_COMMON_CMM_FILE;
    tag = ( uint32_t ) rand(  );
    offset = ( uint64_t ) ( rand(  ) % TEST_COMMON_CMM_FILE );

    if ( XSUCCESS != xrn_estimate_block_bytes_length( offset, &iblock0_len ) ) {
        return 1;
    }

    ibytes = ( uint8_t * ) malloc( ibytes_len * sizeof( uint8_t ) );
    iblock0 = ( uint8_t * ) malloc( iblock0_len * sizeof( uint8_t ) );
    iblock1 = ( uint8_t * ) malloc( iblock0_len * sizeof( uint8_t ) );

    // create input bytes

    for ( i = 0; i < ibytes_len; i++ ) {
        ibytes[i] = ( uint8_t ) rand(  ) % 256;
    }

    // create two non continous blocks

    if ( XSUCCESS != xrn_make_block_bytes( ibytes, ibytes_len, iblock0, iblock0_len, tag, 0, offset ) ) {
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        return 1;
    }

    fpi = fopen( "../cache/to_remove_test_merge.txt", "wb" );

    // write the file
    if ( fwrite( ibytes, ibytes_len, 1, fpi ) != 1 ) {
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        fclose( fpi );
        return 1;
    }

    fclose( fpi );

    // create block

    fpi = fopen( "../cache/to_remove_test_merge.txt", "rb" );
    fpb0 = fopen( "../cache/to_remove_test_merge_file0.txt", "wb" );

    if ( XSUCCESS != xrn_make_block( fpi, fpb0, tag, 0, offset ) ) {
        remove( "../cache/to_remove_test_merge.txt" );
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        fclose( fpi );
        fclose( fpb0 );
        return 1;
    }

    fclose( fpi );
    fclose( fpb0 );

    // read the file

    fpb0 = fopen( "../cache/to_remove_test_merge_file0.txt", "rb" );
    if ( 1 != fread( iblock1, iblock0_len, 1, fpb0 ) ) {
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        fclose( fpb0 );
        remove( "../cache/to_remove_test_merge.txt" );
        remove( "../cache/to_remove_test_merge_file0.txt" );
        return 1;
    }

    fclose( fpb0 );

    error = 0;
    for ( i = 0; i < iblock0_len; i++ ) {
        error = error || ( iblock1[i] != iblock0[i] );
    }

    remove( "../cache/to_remove_test_merge.txt" );
    remove( "../cache/to_remove_test_merge_file0.txt" );
    free( ibytes );
    free( iblock0 );
    free( iblock1 );

    if ( error != 0 ) {
        return 1;
    }

    return 0;

}

int test_merge_complete(
 )
{

    FILE *fpi;
    FILE *fpb0;
    FILE *fpb1;
    FILE *fpo;
    FILE *fps;

    FILE *fp2[XRN_MAX_MERGE_PARALLELISM];
    uint8_t buffer[TEST_COMMON_CMM_FILE], num_blocks;
    int i;
    uint32_t tag;
    uint64_t offset;
    int error, cnt0, cnt1;
    uint8_t ch1, ch2;

    tag = ( uint32_t ) rand(  );

    // create input file

    for ( i = 0; i < TEST_COMMON_CMM_FILE; i++ ) {
        buffer[i] = ( uint8_t ) rand(  ) % 256;
    }

    fpi = fopen( "../cache/to_remove_test_merge.txt", "wb" );

    // write the file
    if ( fwrite( buffer, TEST_COMMON_CMM_FILE, 1, fpi ) != 1 ) {
        fclose( fpi );
        return 1;
    }

    fclose( fpi );

    // create two continous blocks

    fpi = fopen( "../cache/to_remove_test_merge.txt", "rb" );
    fpb0 = fopen( "../cache/to_remove_test_merge_file0.txt", "wb" );

    offset = ( uint64_t ) ( rand(  ) % TEST_COMMON_CMM_FILE );
    if ( XSUCCESS != xrn_make_block( fpi, fpb0, tag, 0, offset ) ) {
        remove( "../cache/to_remove_test_merge.txt" );
        fclose( fpi );
        fclose( fpb0 );
        return 1;
    }

    fclose( fpi );
    fclose( fpb0 );

    fpi = fopen( "../cache/to_remove_test_merge.txt", "rb" );
    fpb1 = fopen( "../cache/to_remove_test_merge_file1.txt", "wb" );

    if ( XSUCCESS != xrn_make_block( fpi, fpb1, tag, offset, TEST_COMMON_CMM_FILE - offset ) ) {
        remove( "../cache/to_remove_test_merge.txt" );
        remove( "../cache/to_remove_test_merge_file0.txt" );
        fclose( fpi );
        fclose( fpb1 );
        return 1;
    }

    fclose( fpi );
    fclose( fpb1 );

    // merge the two blocks and do not exptect any fault

    fpb0 = fopen( "../cache/to_remove_test_merge_file0.txt", "rb" );
    fpb1 = fopen( "../cache/to_remove_test_merge_file1.txt", "rb" );
    fpo = fopen( "../cache/to_remove_test_merge_block.txt", "wb" );

    fp2[0] = fpb0;
    fp2[1] = fpb1;
    num_blocks = 2;
    if ( XSUCCESS != xrn_join_blocks( fp2, num_blocks, fpo ) ) {
        remove( "../cache/to_remove_test_merge.txt" );
        remove( "../cache/to_remove_test_merge_file0.txt" );
        fclose( fpo );
        fclose( fpb0 );
        fclose( fpb1 );
        remove( "../cache/to_remove_test_merge_file1.txt" );
        return 1;
    }

    fclose( fpo );
    fclose( fpb0 );
    fclose( fpb1 );

    // check if the file is complete

    fpo = fopen( "../cache/to_remove_test_merge_block.txt", "rb" );
    if ( XNCOMPLETE == xrn_check_complete_blocks( fpo ) ) {
        remove( "../cache/to_remove_test_merge.txt" );
        remove( "../cache/to_remove_test_merge_file0.txt" );
        remove( "../cache/to_remove_test_merge_file1.txt" );
        remove( "../cache/to_remove_test_merge_block.txt" );
        return 1;
    }

    fclose( fpo );

    // strip header

    fps = fopen( "../cache/to_remove_test_merge_block_strip.txt", "wb" );
    fpo = fopen( "../cache/to_remove_test_merge_block.txt", "rb" );

    if ( XSUCCESS != xrn_strip_header_from_block( fpo, fps ) ) {
        remove( "../cache/to_remove_test_merge.txt" );
        remove( "../cache/to_remove_test_merge_file0.txt" );
        remove( "../cache/to_remove_test_merge_file1.txt" );
        remove( "../cache/to_remove_test_merge_block.txt" );
        fclose( fpo );
        fclose( fps );
        return 1;
    }

    fclose( fpo );
    fclose( fps );

    // check if the stripped file is equal to the input file

    fpi = fopen( "../cache/to_remove_test_merge.txt", "rb" );
    fps = fopen( "../cache/to_remove_test_merge_block_strip.txt", "rb" );
    error = 0;

    do {
        // Input character from both files
        cnt0 = ( int ) fread( &ch1, sizeof( uint8_t ), 1, fpi );
        cnt1 = ( int ) fread( &ch2, sizeof( uint8_t ), 1, fps );
        error = ( ( ch1 != ch2 ) || ( cnt0 != cnt1 ) ) || error;

    } while ( cnt0 != 0 && cnt1 != 0 );

    fclose( fpi );
    fclose( fps );

    remove( "../cache/to_remove_test_merge.txt" );
    remove( "../cache/to_remove_test_merge_file0.txt" );
    remove( "../cache/to_remove_test_merge_file1.txt" );
    remove( "../cache/to_remove_test_merge_block.txt" );
    remove( "../cache/to_remove_test_merge_block_strip.txt" );

    if ( error != 0 ) {
        return 1;
    }

    return 0;

}

int test_merge_partial_bytes(
 )
{
    uint8_t *ibytes;
    uint8_t *iblock0;
    uint8_t *iblock1;
    uint8_t *oblock;

    uint64_t ibytes_len;
    uint64_t iblock0_len;
    uint64_t iblock1_len;
    uint64_t oblock_len;
    uint8_t num_inputs;

    uint8_t *iblocks[XRN_MAX_MERGE_PARALLELISM];
    uint64_t iblocks_len[XRN_MAX_MERGE_PARALLELISM];

    int i;
    uint32_t tag;
    uint64_t offset;

    ibytes_len = TEST_COMMON_CMM_FILE;
    tag = ( uint32_t ) rand(  );
    offset = ( uint64_t ) ( rand(  ) % TEST_COMMON_CMM_FILE );

    if ( XSUCCESS != xrn_estimate_block_bytes_length( offset, &iblock0_len ) ) {
        return 1;
    }

    if ( XSUCCESS != xrn_estimate_block_bytes_length( TEST_COMMON_CMM_FILE - ( offset + 2 ), &iblock1_len ) ) {
        return 1;
    }

    iblocks_len[0] = iblock0_len;
    iblocks_len[1] = iblock1_len;
    num_inputs = 2;

    if ( XSUCCESS != xrn_estimate_block_bytes_length_merge( iblocks_len, num_inputs, &oblock_len ) ) {
        return 1;
    }

    ibytes = ( uint8_t * ) malloc( ibytes_len * sizeof( uint8_t ) );
    iblock0 = ( uint8_t * ) malloc( iblock0_len * sizeof( uint8_t ) );
    iblock1 = ( uint8_t * ) malloc( iblock1_len * sizeof( uint8_t ) );
    oblock = ( uint8_t * ) malloc( oblock_len * sizeof( uint8_t ) );

    // create input bytes

    for ( i = 0; i < TEST_COMMON_CMM_FILE; i++ ) {
        ibytes[i] = ( uint8_t ) rand(  ) % 256;
    }

    for ( i = 0; i < oblock_len; i++ ) {
        oblock[i] = 0;
    }

    // create two non continous blocks

    if ( XSUCCESS != xrn_make_block_bytes( ibytes, ibytes_len, iblock0, iblock0_len, tag, 0, offset ) ) {
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        free( oblock );
        return 1;
    }

    if ( XSUCCESS !=
         xrn_make_block_bytes( ibytes, ibytes_len, iblock1, iblock1_len, tag, offset + 1,
                               TEST_COMMON_CMM_FILE - ( offset + 2 ) ) ) {
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        free( oblock );
        return 1;
    }

    // merge the two blocks and exptect fault

    iblocks[0] = iblock0;
    iblocks[1] = iblock1;
    iblocks_len[0] = iblock0_len;
    iblocks_len[1] = iblock1_len;
    num_inputs = 2;

    if ( XSUCCESS == xrn_join_blocks_bytes( iblocks, iblocks_len, num_inputs, oblock, oblock_len ) ) {
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        free( oblock );
        return 1;
    }

    // check if the file is complete

    if ( XCOMPLETE == xrn_check_complete_blocks_bytes( oblock, oblock_len ) ) {
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        free( oblock );
        return 1;
    }

    free( ibytes );
    free( iblock0 );
    free( iblock1 );
    free( oblock );

    return 0;

}

int test_merge_complete_bytes(
 )
{

    uint8_t *ibytes;
    uint8_t *iblock0;
    uint8_t *iblock1;
    uint8_t *oblock;
    uint8_t *stripb;

    uint64_t ibytes_len;
    uint64_t iblock0_len;
    uint64_t iblock1_len;
    uint64_t oblock_len;
    uint64_t stripb_len;

    uint8_t *iblocks[XRN_MAX_MERGE_PARALLELISM];
    uint64_t iblocks_len[XRN_MAX_MERGE_PARALLELISM];
    uint8_t num_inputs;

    int i;
    uint32_t tag;
    uint64_t offset;
    int error;

    tag = ( uint32_t ) rand(  );

    ibytes_len = TEST_COMMON_CMM_FILE;
    offset = ( uint64_t ) ( rand(  ) % TEST_COMMON_CMM_FILE );

    if ( XSUCCESS != xrn_estimate_block_bytes_length( offset, &iblock0_len ) ) {
        return 1;
    }

    if ( XSUCCESS != xrn_estimate_block_bytes_length( TEST_COMMON_CMM_FILE - offset, &iblock1_len ) ) {
        return 1;
    }

    iblocks_len[0] = iblock0_len;
    iblocks_len[1] = iblock1_len;
    num_inputs = 2;

    if ( XSUCCESS != xrn_estimate_block_bytes_length_merge( iblocks_len, num_inputs, &oblock_len ) ) {
        return 1;
    }

    if ( XSUCCESS != xrn_estimate_block_bytes_length_strip( oblock_len, &stripb_len ) ) {
        return 1;
    }

    ibytes = ( uint8_t * ) malloc( ibytes_len * sizeof( uint8_t ) );
    iblock0 = ( uint8_t * ) malloc( iblock0_len * sizeof( uint8_t ) );
    iblock1 = ( uint8_t * ) malloc( iblock1_len * sizeof( uint8_t ) );
    oblock = ( uint8_t * ) malloc( oblock_len * sizeof( uint8_t ) );
    stripb = ( uint8_t * ) malloc( stripb_len * sizeof( uint8_t ) );

    // create input bytes

    for ( i = 0; i < TEST_COMMON_CMM_FILE; i++ ) {
        ibytes[i] = ( uint8_t ) rand(  ) % 256;
    }

    // create blocks

    if ( XSUCCESS != xrn_make_block_bytes( ibytes, ibytes_len, iblock0, iblock0_len, tag, 0, offset ) ) {
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        free( oblock );
        free( stripb );
        return 1;
    }

    if ( XSUCCESS !=
         xrn_make_block_bytes( ibytes, ibytes_len, iblock1, iblock1_len, tag, offset,
                               TEST_COMMON_CMM_FILE - offset ) ) {
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        free( oblock );
        free( stripb );
        return 1;
    }

    // merge bytes

    iblocks[0] = iblock0;
    iblocks[1] = iblock1;
    iblocks_len[0] = iblock0_len;
    iblocks_len[1] = iblock1_len;
    num_inputs = 2;
    if ( XSUCCESS != xrn_join_blocks_bytes( iblocks, iblocks_len, num_inputs, oblock, oblock_len ) ) {
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        free( oblock );
        free( stripb );
        return 1;
    }

    // check if the file is complete

    if ( XNCOMPLETE == xrn_check_complete_blocks_bytes( oblock, oblock_len ) ) {
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        free( oblock );
        free( stripb );
        return 1;
    }

    // strip header

    if ( XSUCCESS != xrn_strip_header_from_block_bytes( oblock, oblock_len, stripb, stripb_len ) ) {
        free( ibytes );
        free( iblock0 );
        free( iblock1 );
        free( oblock );
        free( stripb );
        return 1;
    }

    // check if the stripped file is equal to the input file

    error = 0;

    for ( i = 0; i < ibytes_len; i++ ) {
        error = error || ( ibytes[i] != stripb[i] );
    }

    free( ibytes );
    free( iblock0 );
    free( iblock1 );
    free( oblock );
    free( stripb );

    if ( error != 0 ) {
        return 1;
    }

    return 0;

}

int test_merge_partial(
 )
{

    FILE *fpi;
    FILE *fpb0;
    FILE *fpb1;
    FILE *fpo;
    FILE *fp2[XRN_MAX_MERGE_PARALLELISM];

    uint8_t buffer[TEST_COMMON_CMM_FILE], num_blocks;
    int i;
    uint32_t tag;
    uint64_t offset;

    tag = ( uint32_t ) rand(  );

    // create input file

    for ( i = 0; i < TEST_COMMON_CMM_FILE; i++ ) {
        buffer[i] = ( uint8_t ) rand(  ) % 256;
    }

    fpi = fopen( "../cache/to_remove_test_merge.txt", "wb" );

    // write the file
    if ( fwrite( buffer, TEST_COMMON_CMM_FILE, 1, fpi ) != 1 ) {
        fclose( fpi );
        return 1;
    }

    fclose( fpi );

    // create two non continous blocks

    fpi = fopen( "../cache/to_remove_test_merge.txt", "rb" );
    fpb0 = fopen( "../cache/to_remove_test_merge_file0.txt", "wb" );

    offset = ( uint64_t ) ( rand(  ) % TEST_COMMON_CMM_FILE );
    if ( XSUCCESS != xrn_make_block( fpi, fpb0, tag, 0, offset ) ) {
        remove( "../cache/to_remove_test_merge.txt" );
        fclose( fpi );
        fclose( fpb0 );
        return 1;
    }

    fclose( fpi );
    fclose( fpb0 );

    fpi = fopen( "../cache/to_remove_test_merge.txt", "rb" );
    fpb1 = fopen( "../cache/to_remove_test_merge_file1.txt", "wb" );

    if ( XSUCCESS != xrn_make_block( fpi, fpb1, tag, offset + 1, TEST_COMMON_CMM_FILE - ( offset + 2 ) ) ) {
        remove( "../cache/to_remove_test_merge.txt" );
        remove( "../cache/to_remove_test_merge_file0.txt" );
        fclose( fpi );
        fclose( fpb1 );
        return 1;
    }

    fclose( fpi );
    fclose( fpb1 );

    // merge the two blocks and exptect fault

    fpb0 = fopen( "../cache/to_remove_test_merge_file0.txt", "rb" );
    fpb1 = fopen( "../cache/to_remove_test_merge_file1.txt", "rb" );
    fpo = fopen( "../cache/to_remove_test_merge_block.txt", "wb" );

    fp2[0] = fpb0;
    fp2[1] = fpb1;
    num_blocks = 2;
    if ( XSUCCESS == xrn_join_blocks( fp2, num_blocks, fpo ) ) {
        fclose( fpo );
        fclose( fpb0 );
        fclose( fpb1 );
        remove( "../cache/to_remove_test_merge_file1.txt" );
        remove( "../cache/to_remove_test_merge.txt" );
        remove( "../cache/to_remove_test_merge_file0.txt" );
        return 1;
    }

    fclose( fpo );
    fclose( fpb0 );
    fclose( fpb1 );

    // check if the file is complete

    fpo = fopen( "../cache/to_remove_test_merge_block.txt", "rb" );
    if ( XCOMPLETE == xrn_check_complete_blocks( fpo ) ) {
        fclose( fpo );
        remove( "../cache/to_remove_test_merge.txt" );
        remove( "../cache/to_remove_test_merge_file0.txt" );
        remove( "../cache/to_remove_test_merge_file1.txt" );
        remove( "../cache/to_remove_test_merge_block.txt" );
        return 1;
    }

    fclose( fpo );

    remove( "../cache/to_remove_test_merge.txt" );
    remove( "../cache/to_remove_test_merge_file0.txt" );
    remove( "../cache/to_remove_test_merge_file1.txt" );
    remove( "../cache/to_remove_test_merge_block.txt" );

    return 0;

}

int positive_negative_test_checksum_dump_bytes(
 )
{

    uint64_t i;
    int ret;

    uint8_t *i_bytes;
    uint64_t i_length;
    uint8_t *o_bytes;
    uint64_t o_length;
    uint64_t o_written_bytes;
    size_t alloc_size;

    // get size of the file
    i_length = ( uint64_t ) ( rand(  ) ) % TEST_COMMON_MAX_FILE;
    ret = xrn_estimate_checksum_bytes_length( i_length, &o_length );
    while ( XSUCCESS != ret ) {
        i_length = ( uint64_t ) ( rand(  ) ) % TEST_COMMON_MAX_FILE;
        ret = xrn_estimate_checksum_bytes_length( i_length, &o_length );
    }

    alloc_size = ( size_t ) ( i_length + 1 ) * sizeof( uint8_t );

    i_bytes = ( uint8_t * ) malloc( alloc_size );
    alloc_size = ( size_t ) ( o_length + 1 ) * sizeof( uint8_t );
    o_bytes = ( uint8_t * ) malloc( alloc_size );

    // initialize the stream of bytes
    for ( i = 0; i < i_length; i++ ) {
        i_bytes[i] = ( uint8_t ) ( rand(  ) % 256 );
    }

    // perform the xrn_make_checksums_wrapper

    ret = xrn_dump_checksums_bytes_wrapper( i_bytes, i_length, o_bytes, o_length, &o_written_bytes );

    if ( ret != XSUCCESS ) {

        free( i_bytes );
        free( o_bytes );
        return 1;
    }

    // check the two outputs

    ret = xrn_check_checksums_bytes_wrapper( i_bytes, i_length, o_bytes, o_length );

    if ( ret != XSUCCESS ) {
        free( i_bytes );
        free( o_bytes );
        return 1;
    }

    // inject error

    i_bytes[( uint64_t ) ( i_length / 2 )] =
        ( uint8_t ) ( ( i_bytes[( uint64_t ) ( i_length / 2 )] + 1 ) % 256 );

    // check the two outputs

    ret = xrn_check_checksums_bytes_wrapper( i_bytes, i_length, o_bytes, o_length );

    if ( ret == XSUCCESS ) {

        free( i_bytes );
        free( o_bytes );
        return 1;
    }

    free( i_bytes );
    free( o_bytes );

    return 0;

}

int positive_negative_test_checksum_dump_file(
 )
{

    FILE *fpi;
    FILE *fpo;

    uint64_t i;
    int ret;

    uint8_t *i_bytes;
    uint64_t i_length;

    size_t alloc_size;

    // get size of the file
    i_length = ( uint64_t ) ( rand(  ) ) % TEST_COMMON_MAX_FILE;
    ret = xrn_estimate_checksum_bytes_length( i_length, &i );
    while ( XSUCCESS != ret ) {
        i_length = ( uint64_t ) ( rand(  ) ) % TEST_COMMON_MAX_FILE;
        ret = xrn_estimate_checksum_bytes_length( i_length, &i );
    }

    alloc_size = ( size_t ) ( i_length + 1 ) * sizeof( uint8_t );

    i_bytes = ( uint8_t * ) malloc( alloc_size );

    // initialize the stream of bytes
    for ( i = 0; i < i_length; i++ ) {
        i_bytes[i] = ( uint8_t ) ( rand(  ) % 256 );
    }

    fpi = fopen( "../cache/to_remove_test_chksum_in.txt", "wb" );
    if ( fpi == NULL ) {
        free( i_bytes );
        return 1;
    }

    // write the file
    ret = ( int ) fwrite( i_bytes, i_length, 1, fpi );

    if ( ret != 1 ) {

        fclose( fpi );
        free( i_bytes );
        return 1;
    }

    fclose( fpi );

    // perform the xrn_make_checksums_wrapper

    fpi = fopen( "../cache/to_remove_test_chksum_in.txt", "rb" );
    fpo = fopen( "../cache/to_remove_test_chksum_out.txt", "wb" );

    if ( ( fpo == NULL ) || ( fpi == NULL ) ) {
        free( i_bytes );
        remove( "../cache/to_remove_test_chksum_in.txt" );
        return 1;
    }

    ret = xrn_make_checksums_wrapper( fpi, fpo );

    if ( ret != XSUCCESS ) {

        fclose( fpi );
        fclose( fpo );
        free( i_bytes );
        remove( "../cache/to_remove_test_chksum_in.txt" );
        return 1;
    }

    fclose( fpi );
    fclose( fpo );

    // check the checksum and expect success

    fpi = fopen( "../cache/to_remove_test_chksum_in.txt", "rb" );
    fpo = fopen( "../cache/to_remove_test_chksum_out.txt", "rb" );

    if ( ( fpo == NULL ) || ( fpi == NULL ) ) {
        free( i_bytes );
        remove( "../cache/to_remove_test_chksum_in.txt" );
        remove( "../cache/to_remove_test_chksum_out.txt" );
        return 1;
    }

    ret = xrn_check_checksums_wrapper( fpi, fpo );

    if ( ret != XSUCCESS ) {

        fclose( fpi );
        fclose( fpo );
        free( i_bytes );
        remove( "../cache/to_remove_test_chksum_in.txt" );
        remove( "../cache/to_remove_test_chksum_out.txt" );
        return 1;
    }

    fclose( fpi );
    fclose( fpo );

    // inject error

    i_bytes[( uint64_t ) ( i_length / 2 )] =
        ( uint8_t ) ( ( i_bytes[( uint64_t ) ( i_length / 2 )] + 1 ) % 256 );

    fpi = fopen( "../cache/to_remove_test_chksum_in.txt", "wb" );
    if ( fpi == NULL ) {
        free( i_bytes );
        return 1;
    }

    ret = ( int ) fwrite( i_bytes, i_length, 1, fpi );

    if ( ret != 1 ) {

        fclose( fpi );
        free( i_bytes );
        return 1;
    }

    fclose( fpi );

    // check and expect error

    fpi = fopen( "../cache/to_remove_test_chksum_in.txt", "rb" );
    fpo = fopen( "../cache/to_remove_test_chksum_out.txt", "rb" );

    if ( ( fpo == NULL ) || ( fpi == NULL ) ) {
        free( i_bytes );
        remove( "../cache/to_remove_test_chksum_in.txt" );
        remove( "../cache/to_remove_test_chksum_out.txt" );
        return 1;
    }

    ret = xrn_check_checksums_wrapper( fpi, fpo );

    if ( ret == XSUCCESS ) {

        fclose( fpi );
        fclose( fpo );
        free( i_bytes );
        remove( "../cache/to_remove_test_chksum_in.txt" );
        remove( "../cache/to_remove_test_chksum_out.txt" );
        return 1;
    }

    fclose( fpi );
    fclose( fpo );

    remove( "../cache/to_remove_test_chksum_in.txt" );
    remove( "../cache/to_remove_test_chksum_out.txt" );
    free( i_bytes );

    return 0;

}

int test_checksum_equivalence(
 )
{

    FILE *fpi;
    FILE *fpo;

    uint64_t i, error;
    int ret;

    uint8_t *i_bytes;
    uint64_t i_length;
    uint8_t *o_bytes;
    uint64_t o_length;
    uint8_t *o_bytes2;
    uint64_t o_written_bytes;
    size_t alloc_size;

    // get size of the file
    i_length = ( uint64_t ) ( rand(  ) ) % TEST_COMMON_MAX_FILE;
    ret = xrn_estimate_checksum_bytes_length( i_length, &o_length );
    while ( XSUCCESS != ret ) {
        i_length = ( uint64_t ) ( rand(  ) ) % TEST_COMMON_MAX_FILE;
        ret = xrn_estimate_checksum_bytes_length( i_length, &o_length );
    }

    alloc_size = ( size_t ) ( i_length + 1 ) * sizeof( uint8_t );

    i_bytes = ( uint8_t * ) malloc( alloc_size );
    alloc_size = ( size_t ) ( o_length + 1 ) * sizeof( uint8_t );
    o_bytes = ( uint8_t * ) malloc( alloc_size );
    o_bytes2 = ( uint8_t * ) malloc( alloc_size );

    // initialize the stream of bytes
    for ( i = 0; i < i_length; i++ ) {
        i_bytes[i] = ( uint8_t ) ( rand(  ) % 256 );
    }

    fpi = fopen( "../cache/to_remove_test_chksum_in.txt", "wb" );
    if ( fpi == NULL ) {
        free( i_bytes );
        free( o_bytes );
        free( o_bytes2 );
        return 1;
    }

    // write the file
    ret = ( int ) fwrite( i_bytes, i_length, 1, fpi );

    if ( ret != 1 ) {

        fclose( fpi );
        free( i_bytes );
        free( o_bytes );
        free( o_bytes2 );
        return 1;
    }

    fclose( fpi );

    // perform the xrn_make_checksums_wrapper

    fpi = fopen( "../cache/to_remove_test_chksum_in.txt", "rb" );
    fpo = fopen( "../cache/to_remove_test_chksum_out.txt", "wb" );

    if ( ( fpo == NULL ) || ( fpi == NULL ) ) {
        free( i_bytes );
        free( o_bytes );
        free( o_bytes2 );
        remove( "../cache/to_remove_test_chksum_in.txt" );
        return 1;
    }

    ret = xrn_make_checksums_wrapper( fpi, fpo );

    if ( ret != XSUCCESS ) {

        fclose( fpi );
        fclose( fpo );
        free( i_bytes );
        free( o_bytes );
        free( o_bytes2 );
        remove( "../cache/to_remove_test_chksum_in.txt" );
        return 1;
    }

    fclose( fpi );
    fclose( fpo );

    ret = xrn_dump_checksums_bytes_wrapper( i_bytes, i_length, o_bytes, o_length, &o_written_bytes );

    if ( ret != XSUCCESS ) {

        free( i_bytes );
        free( o_bytes );
        free( o_bytes2 );
        remove( "../cache/to_remove_test_chksum_in.txt" );
        remove( "../cache/to_remove_test_chksum_out.txt" );
        return 1;
    }

    // check the two outputs

    fpo = fopen( "../cache/to_remove_test_chksum_out.txt", "rb" );
    ret = ( int ) fread( o_bytes2, 1, o_written_bytes, fpo );

    if ( ret != o_written_bytes ) {
        fclose( fpo );
        free( i_bytes );
        free( o_bytes );
        free( o_bytes2 );
        remove( "../cache/to_remove_test_chksum_in.txt" );
        remove( "../cache/to_remove_test_chksum_out.txt" );
        return 1;
    }

    fclose( fpo );

    // do the equivalence check

    error = 0;
    for ( i = 0; i < o_written_bytes; i++ ) {
        error = error || ( o_bytes[i] != o_bytes2[i] );

    }

    free( i_bytes );
    free( o_bytes );
    free( o_bytes2 );
    remove( "../cache/to_remove_test_chksum_in.txt" );
    remove( "../cache/to_remove_test_chksum_out.txt" );

    if ( error != 0 ) {
        return 1;
    }

    return 0;

}

int test_all_xrn_common( FILE * fp, int runs, int non_block
 )
{
    time_t t;
    char buff[256];

    int v_positive_negative_test_checksum_dump_file;
    int v_test_checksum_equivalence;
    int v_positive_negative_test_checksum_dump_bytes;
    int v_test_merge_complete;
    int v_test_merge_partial;
    int v_test_merge_complete_bytes;
    int v_test_merge_partial_bytes;
    int v_test_split_block_comp;

    srand( ( unsigned ) time( &t ) );
    int error;
    int occ;

    error = 0;

    for ( occ = 0; ( occ < runs ) && ( ( error == 0 ) || non_block ); occ++ ) {
        srand( ( unsigned ) time( &t ) + ( unsigned ) occ );
        v_test_split_block_comp = test_split_block_comp(  );
        error += v_test_split_block_comp;
        printf( "[%4d]", occ );
        if ( !v_test_split_block_comp ) {
            printf( "test_xrn_common -> test_split_block_comp passed\n" );
            fprintf( fp, "test_xrn_common -> test_split_block_comp passed\n" );
        } else {
            printf( "test_xrn_common -> test_split_block_comp failed\n" );
            fprintf( fp, "test_xrn_common -> test_split_block_comp failed\n" );
        }

        if ( v_test_split_block_comp != 0 ) {
#if ( XRN_GNU_LINUX == 1 )
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %ld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %ld \n", t );
#else
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %lld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %lld \n", t );
#endif
            printf( "%s", buff );
        }

    }

    for ( occ = 0; ( occ < runs ) && ( ( error == 0 ) || non_block ); occ++ ) {
        srand( ( unsigned ) time( &t ) + ( unsigned ) occ );
        v_test_merge_complete_bytes = test_merge_complete_bytes(  );
        error += v_test_merge_complete_bytes;
        printf( "[%4d]", occ );
        if ( !v_test_merge_complete_bytes ) {
            printf( "test_xrn_common -> test_merge_complete_bytes passed\n" );
            fprintf( fp, "test_xrn_common -> test_merge_complete_bytes passed\n" );
        } else {
            printf( "test_xrn_common -> test_merge_complete_bytes failed\n" );
            fprintf( fp, "test_xrn_common -> test_merge_complete_bytes failed\n" );
        }

        if ( v_test_merge_complete_bytes != 0 ) {
#if ( XRN_GNU_LINUX == 1 )
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %ld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %ld \n", t );
#else
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %lld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %lld \n", t );
#endif
            printf( "%s", buff );
        }

    }

    for ( occ = 0; ( occ < runs ) && ( ( error == 0 ) || non_block ); occ++ ) {
        srand( ( unsigned ) time( &t ) + ( unsigned ) occ );
        v_test_merge_partial_bytes = test_merge_partial_bytes(  );
        error += v_test_merge_partial_bytes;
        printf( "[%4d]", occ );
        if ( !v_test_merge_partial_bytes ) {
            printf( "test_xrn_common -> test_merge_partial_bytes passed\n" );
            fprintf( fp, "test_xrn_common -> test_merge_partial_bytes passed\n" );
        } else {
            printf( "test_xrn_common -> test_merge_partial_bytes failed\n" );
            fprintf( fp, "test_xrn_common -> test_merge_partial_bytes failed\n" );
        }

        if ( v_test_merge_partial_bytes != 0 ) {
#if ( XRN_GNU_LINUX == 1 )
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %ld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %ld \n", t );
#else
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %lld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %lld \n", t );
#endif
            printf( "%s", buff );
        }

    }

    for ( occ = 0; ( occ < runs ) && ( ( error == 0 ) || non_block ); occ++ ) {
        srand( ( unsigned ) time( &t ) + ( unsigned ) occ );
        v_test_merge_complete = test_merge_complete(  );
        error += v_test_merge_complete;
        printf( "[%4d]", occ );
        if ( !v_test_merge_complete ) {
            printf( "test_xrn_common -> test_merge_complete passed\n" );
            fprintf( fp, "test_xrn_common -> test_merge_complete passed\n" );
        } else {
            printf( "test_xrn_common -> test_merge_complete failed\n" );
            fprintf( fp, "test_xrn_common -> test_merge_complete failed\n" );
        }

        if ( v_test_merge_complete != 0 ) {
#if ( XRN_GNU_LINUX == 1 )
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %ld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %ld \n", t );
#else
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %lld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %lld \n", t );
#endif
            printf( "%s", buff );
        }

    }

    for ( occ = 0; ( occ < runs ) && ( ( error == 0 ) || non_block ); occ++ ) {
        srand( ( unsigned ) time( &t ) + ( unsigned ) occ );
        v_test_merge_partial = test_merge_partial(  );
        error += v_test_merge_partial;
        printf( "[%4d]", occ );
        if ( !v_test_merge_partial ) {
            printf( "test_xrn_common -> test_merge_partial passed\n" );
            fprintf( fp, "test_xrn_common -> test_merge_partial passed\n" );
        } else {
            printf( "test_xrn_common -> test_merge_partial failed\n" );
            fprintf( fp, "test_xrn_common -> test_merge_partial failed\n" );
        }

        if ( v_test_merge_partial != 0 ) {
#if ( XRN_GNU_LINUX == 1 )
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %ld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %ld \n", t );
#else
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %lld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %lld \n", t );
#endif
            printf( "%s", buff );
        }

    }

    for ( occ = 0; ( occ < runs ) && ( ( error == 0 ) || non_block ); occ++ ) {
        srand( ( unsigned ) time( &t ) + ( unsigned ) occ );
        v_positive_negative_test_checksum_dump_bytes = positive_negative_test_checksum_dump_bytes(  );
        error += v_positive_negative_test_checksum_dump_bytes;
        printf( "[%4d]", occ );
        if ( !v_positive_negative_test_checksum_dump_bytes ) {
            printf( "test_xrn_common -> positive_negative_test_checksum_dump_bytes passed\n" );
            fprintf( fp, "test_xrn_common -> positive_negative_test_checksum_dump_bytes passed\n" );
        } else {
            printf( "test_xrn_common -> positive_negative_test_checksum_dump_bytes failed\n" );
            fprintf( fp, "test_xrn_common -> positive_negative_test_checksum_dump_bytes failed\n" );
        }

        if ( v_positive_negative_test_checksum_dump_bytes != 0 ) {
#if ( XRN_GNU_LINUX == 1 )
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %ld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %ld \n", t );
#else
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %lld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %lld \n", t );
#endif
            printf( "%s", buff );
        }

    }

    for ( occ = 0; ( occ < runs ) && ( ( error == 0 ) || non_block ); occ++ ) {
        srand( ( unsigned ) time( &t ) + ( unsigned ) occ );
        v_positive_negative_test_checksum_dump_file = positive_negative_test_checksum_dump_file(  );
        error += v_positive_negative_test_checksum_dump_file;
        printf( "[%4d]", occ );
        if ( !v_positive_negative_test_checksum_dump_file ) {
            printf( "test_xrn_common -> positive_negative_test_checksum_dump_file passed\n" );
            fprintf( fp, "test_xrn_common -> positive_negative_test_checksum_dump_file passed\n" );
        } else {
            printf( "test_xrn_common -> positive_negative_test_checksum_dump_file failed\n" );
            fprintf( fp, "test_xrn_common -> positive_negative_test_checksum_dump_file failed\n" );
        }

        if ( v_positive_negative_test_checksum_dump_file != 0 ) {
#if ( XRN_GNU_LINUX == 1 )
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %ld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %ld \n", t );
#else
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %lld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %lld \n", t );
#endif
            printf( "%s", buff );
        }

    }

    for ( occ = 0; ( occ < runs ) && ( ( error == 0 ) || non_block ); occ++ ) {
        srand( ( unsigned ) time( &t ) + ( unsigned ) occ );
        v_test_checksum_equivalence = test_checksum_equivalence(  );
        error += v_test_checksum_equivalence;
        printf( "[%4d]", occ );
        if ( !v_test_checksum_equivalence ) {
            printf( "test_xrn_common -> test_checksum_equivalence passed\n" );
            fprintf( fp, "test_xrn_common -> test_checksum_equivalence passed\n" );
        } else {
            printf( "test_xrn_common -> test_checksum_equivalence failed\n" );
            fprintf( fp, "test_xrn_common -> test_checksum_equivalence failed\n" );
        }

        if ( v_test_checksum_equivalence != 0 ) {
#if ( XRN_GNU_LINUX == 1 )
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %ld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %ld \n", t );
#else
            sprintf( buff, "TEST_CORE_LIB FAILED SEED %lld ", t );
            fprintf( fp, "TEST_CORE_LIB FAILED SEED %lld \n", t );
#endif
            printf( "%s", buff );
        }

    }

    return error;
}

